
package org.spiderflow.core.expression.interpreter;

import java.io.IOException;
import java.util.List;

import org.spiderflow.core.exception.TemplateException;
import org.spiderflow.core.expression.ExpressionError;
import org.spiderflow.core.expression.ExpressionTemplate;
import org.spiderflow.core.expression.ExpressionTemplateContext;
import org.spiderflow.core.expression.parsing.ast.Node;
import org.spiderflow.core.expression.parsing.ast.Text;

/**
 * <p>
 * Interprets a Template given a TemplateContext to lookup variable values in and writes the evaluation results to an output
 * stream. Uses the global {@link org.spiderflow.core.expression.reflection.JavaReflection} instance as returned by {@link org.spiderflow.core.expression.reflection.JavaReflection#getInstance()}
 * to access members and call methods.
 * </p>
 *
 * <p>
 * The interpreter traverses the AST as stored in {@link ExpressionTemplate#getNodes()}.
 * the interpreter has a method for each AST node type
 * (see {@link Node} that evaluates that node. A node may return a value,
 * to be used in the interpretation of a parent node or to be written to the output stream.
 * </p>
 *
 * @author zsh
 */
public class AstInterpreter {
    private AstInterpreter() {
    }

    public static Object interpret(ExpressionTemplate template, ExpressionTemplateContext context) {
        try {
            return interpretNodeList(template.getNodes(), template, context);
        } catch (TemplateException t) {
            throw t;
        } catch (IOException | RuntimeException e) {
            throw ExpressionError.error("执行表达式出错 " + e.getMessage(), template.getNodes().get(0).getSpan(), e);
        }
    }


    public static Object interpretNodeList(List<Node> nodes, ExpressionTemplate template, ExpressionTemplateContext context) throws IOException {
        StringBuilder result = new StringBuilder();
        for (int i = 0, n = nodes.size(); i < n; i++) {
            Node node = nodes.get(i);
            Object value = node.evaluate(template, context);
            if (node instanceof Text) {
                result.append(node.getSpan().getText());
            } else if (i == 0 && n == 1) {
                return value;
            } else if (value == null) {
                result.append("null");
            } else if (value instanceof String || value instanceof Number || value instanceof Boolean) {
                result.append(value);
            } else if (i + 1 < n) {
                throw ExpressionError.error("表达式执行错误", node.getSpan());
            } else {
                return value;
            }
        }
        return result.toString();
    }
}
